<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf8">
    <!--

    @license twgl.js Copyright (c) 2015, Gregg Tavares All Rights Reserved.
    Available via the MIT license.
    see: http://github.com/greggman/twgl.js for details

    -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <meta property="og:title" content="TWGL.js - textures" />
    <meta property="og:type" content="website" />
    <meta property="og:image" content="http://twgljs.org/examples/screenshots/textures.png" />
    <meta property="og:description" content="TWGL.js - textures" />
    <meta property="og:url" content="http://twgljs.org" />

    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="@greggman">
    <meta name="twitter:creator" content="@greggman">
    <meta name="twitter:domain" content="twgljs.org">
    <meta name="twitter:title" content="TWGL.js - textures">
    <meta name="twitter:url" content="http://twgljs.org/examples/textures.html">
    <meta name="twitter:description" content="TWGL.js - textures">
    <meta name="twitter:image:src" content="http://twgljs.org/examples/screenshots/textures.png">

    <link href="/resources/images/twgljs-icon.png" rel="shortcut icon" type="image/png">

    <title>twgl.js - textures</title>
    <style>
      body {
          margin: 0;
          font-family: monospace;
      }
      canvas {
          display: block;
          width: 100vw;
          height: 100vh;
      }
      #b {
        position: absolute;
        top: 10px;
        width: 100%;
        text-align: center;
        z-index: 2;
      }
    </style>
  </head>
  <body>
    <canvas id="c"></canvas>
    <div id="b"><a href="http://twgljs.org">twgl.js</a> - textures</div>
  </body>
  <script id="one-point-vs" type="notjs">
uniform mat4 u_worldViewProjection;

attribute vec4 a_position;
attribute vec2 a_texcoord;

varying vec4 v_position;
varying vec2 v_texCoord;

void main() {
  v_texCoord = a_texcoord;
  gl_Position = u_worldViewProjection * a_position;
}
  </script>
  <script id="one-point-fs" type="notjs">
precision mediump float;

varying vec4 v_position;
varying vec2 v_texCoord;

uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;

void main() {
  vec4 diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult;
  if (diffuseColor.a < 0.1) {
    discard;
  }
  gl_FragColor = diffuseColor;
}
  </script>
  <script id="env-map-vs" type="notjs">
uniform mat4 u_viewInverse;
uniform mat4 u_world;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;

attribute vec4 a_position;
attribute vec3 a_normal;

varying vec3 v_normal;
varying vec3 v_surfaceToView;

void main() {
  v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
  v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;
  gl_Position = u_worldViewProjection * a_position;
}
  </script>
  <script id="env-map-fs" type="notjs">
precision mediump float;

uniform samplerCube u_texture;

varying vec3 v_surfaceToView;
varying vec3 v_normal;

void main() {
  vec3 normal = normalize(v_normal);
  vec3 surfaceToView = normalize(v_surfaceToView);
  vec4 color = textureCube(u_texture, -reflect(surfaceToView, normal));
  gl_FragColor = color;
}
  </script>
  <script src="../3rdparty/chroma.min.js"></script>
  <script type="module">
    import * as twgl from '../dist/4.x/twgl-full.module.js';
    twgl.setDefaults({attribPrefix: "a_"});
    const m4 = twgl.m4;
    const gl = document.querySelector("#c").getContext("webgl");
    const onePointProgramInfo = twgl.createProgramInfo(gl, ["one-point-vs", "one-point-fs"]);
    const envMapProgramInfo = twgl.createProgramInfo(gl, ["env-map-vs", "env-map-fs"]);

    const shapes = [
      twgl.primitives.createCubeBufferInfo(gl, 2),
      twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),
      twgl.primitives.createPlaneBufferInfo(gl, 2, 2),
      twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),
    ];

    function rand(min, max) {
      if (max === undefined) {
        max = min;
        min = 0;
      }
      return min + Math.random() * (max - min);
    }

    // Shared values
    const baseHue = rand(360);
    const camera = m4.identity();
    const view = m4.identity();
    const viewProjection = m4.identity();

    // A circle on a canvas
    const ctx = document.createElement("canvas").getContext("2d");
    ctx.canvas.width  = 64;
    ctx.canvas.height = 64;

    function updateCanvas(time) {
      ctx.fillStyle = "#00f";
      ctx.strokeStyle = "#ff0";
      ctx.lineWidth = "10";
      ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      ctx.beginPath();
      ctx.arc(ctx.canvas.width / 2, ctx.canvas.height / 2, ctx.canvas.width / 2.2 * Math.abs(Math.cos(time)), 0, Math.PI * 2);
      ctx.stroke();
    }
    updateCanvas(0);

    // A cubemap drawn to a canvas with a circle on each face.
    const cubemapCtx = document.createElement("canvas").getContext("2d");
    const size = 40;
    cubemapCtx.canvas.width  = size * 6;
    cubemapCtx.canvas.height = size;
    cubemapCtx.fillStyle = "#888";
    for (let ff = 0; ff < 6; ++ff) {
      const color = chroma.hsv((baseHue + ff * 10) % 360, 1 - ff / 6, 1);
      cubemapCtx.fillStyle = color.darken().hex();
      cubemapCtx.fillRect(size * ff, 0, size, size);
      cubemapCtx.save();
      cubemapCtx.translate(size * (ff + 0.5), size * 0.5);
      cubemapCtx.beginPath();
      cubemapCtx.arc(0, 0, size * 0.3, 0, Math.PI * 2);
      cubemapCtx.fillStyle = color.hex();
      cubemapCtx.fill();
      cubemapCtx.restore();
    }

    // make 6 canvases to show loading from 6 element
    const cubeFaceCanvases = [];
    for (let ff = 0; ff < 6; ++ff) {
      const canvas = document.createElement("canvas");
      canvas.width = 128;
      canvas.height = 128;
      const ctx = canvas.getContext("2d");
      const color = chroma.hsv((baseHue + ff * 10) % 360, 1 - ff / 6, 1);
      ctx.fillStyle = color.darken().hex();
      ctx.fillRect(0, 0, 128, 128);
      ctx.translate(64, 64);
      ctx.rotate(Math.PI * .25);
      ctx.fillStyle = color.hex();
      ctx.fillRect(-40, -40, 80, 80);
      cubeFaceCanvases.push(canvas);
    }

    const textures = twgl.createTextures(gl, {
      // a power of 2 image
      hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST },
      // a non-power of 2 image
      clover: { src: "images/clover.jpg" },
      // From a canvas
      fromCanvas: { src: ctx.canvas },
      // A cubemap from 6 images
      yokohama: {
        target: gl.TEXTURE_CUBE_MAP,
        src: [
          'images/yokohama/posx.jpg',
          'images/yokohama/negx.jpg',
          'images/yokohama/posy.jpg',
          'images/yokohama/negy.jpg',
          'images/yokohama/posz.jpg',
          'images/yokohama/negz.jpg',
        ],
      },
      // A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
      goldengate: {
        target: gl.TEXTURE_CUBE_MAP,
        src: 'images/goldengate.jpg',
      },
      // A 2x2 pixel texture from a JavaScript array
      checker: {
        mag: gl.NEAREST,
        min: gl.LINEAR,
        src: [
          255, 255, 255, 255,
          192, 192, 192, 255,
          192, 192, 192, 255,
          255, 255, 255, 255,
        ],
      },
      // a 1x8 pixel texture from a typed array.
      stripe: {
        mag: gl.NEAREST,
        min: gl.LINEAR,
        format: gl.LUMINANCE,
        src: new Uint8Array([
          255,
          128,
          255,
          128,
          255,
          128,
          255,
          128,
        ]),
        width: 1,
      },
      // a cubemap from array
      cubemapFromArray: {
        target: gl.TEXTURE_CUBE_MAP,
        format: gl.RGBA,
        src: [
          0xF0, 0x80, 0x80, 0xFF,
          0x80, 0xE0, 0x80, 0xFF,
          0x80, 0x80, 0xD0, 0xFF,
          0xC0, 0x80, 0x80, 0xFF,
          0x80, 0xB0, 0x80, 0xFF,
          0x80, 0x80, 0x00, 0xFF,
        ],
      },
      cubemapFromCanvas: { target: gl.TEXTURE_CUBE_MAP, src: cubemapCtx.canvas },
      cubemapFrom6Canvases: { target: gl.TEXTURE_CUBE_MAP, src: cubeFaceCanvases },
    });

    // This is soley to make it easy to pick textures at random
    const twoDTextures = [
      textures.checker,
      textures.stripe,
      textures.hftIcon,
      textures.clover,
      textures.fromCanvas,
    ];

    const cubeTextures = [
      textures.yokohama,
      textures.goldengate,
      textures.cubemapFromCanvas,
      textures.cubemapFrom6Canvases,
      textures.cubemapFromArray,
    ];

    const objects = [];
    const drawObjects = [];
    const numObjects = 100;
    for (let ii = 0; ii < numObjects; ++ii) {
      let uniforms;
      let programInfo;
      let shape;
      const renderType = rand(0, 2) | 0;
      switch (renderType) {
        case 0:  // checker
          shape = shapes[ii % shapes.length];
          programInfo = onePointProgramInfo;
          uniforms = {
            u_diffuseMult: chroma.hsv((baseHue + rand(0, 60)) % 360, 0.4, 0.8).gl(),
            u_diffuse: twoDTextures[rand(0, twoDTextures.length) | 0],
            u_viewInverse: camera,
            u_world: m4.identity(),
            u_worldInverseTranspose: m4.identity(),
            u_worldViewProjection: m4.identity(),
          };
          break;
        case 1:  // yokohama
          shape = rand(0, 2) < 1 ? shapes[1] : shapes[3];
          programInfo = envMapProgramInfo;
          uniforms = {
            u_texture: cubeTextures[rand(0, cubeTextures.length) | 0],
            u_viewInverse: camera,
            u_world: m4.identity(),
            u_worldInverseTranspose: m4.identity(),
            u_worldViewProjection: m4.identity(),
          };
          break;
        default:
          throw "wAT!";
      }
      drawObjects.push({
        programInfo: programInfo,
        bufferInfo: shape,
        uniforms: uniforms,
      });
      objects.push({
        translation: [rand(-10, 10), rand(-10, 10), rand(-10, 10)],
        ySpeed: rand(0.1, 0.3),
        zSpeed: rand(0.1, 0.3),
        uniforms: uniforms,
      });
    }

    function render(time) {
      time *= 0.001;
      twgl.resizeCanvasToDisplaySize(gl.canvas);
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

      gl.enable(gl.DEPTH_TEST);
      gl.enable(gl.BLEND);
      gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      const radius = 20;
      const orbitSpeed = time * 0.1;
      const projection = m4.perspective(30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 100);
      const eye = [Math.cos(orbitSpeed) * radius, 4, Math.sin(orbitSpeed) * radius];
      const target = [0, 0, 0];
      const up = [0, 1, 0];

      m4.lookAt(eye, target, up, camera);
      m4.inverse(camera, view);
      m4.multiply(projection, view, viewProjection);

      updateCanvas(time);
      twgl.setTextureFromElement(gl, textures.fromCanvas, ctx.canvas);

      objects.forEach(function(obj) {
        const uni = obj.uniforms;
        const world = uni.u_world;
        m4.identity(world);
        m4.rotateY(world, time * obj.ySpeed, world);
        m4.rotateZ(world, time * obj.zSpeed, world);
        m4.translate(world, obj.translation, world);
        m4.rotateX(world, time, world);
        m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);
        m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);
      });

      twgl.drawObjectList(gl, drawObjects);

      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);

  </script>
</html>




